#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import os
import struct
import copy
import time
import cPickle
import math

import pygtk
try:
    import pygtk
    pygtk.require("2.22")
except:
    pass
import gtk
import cairo
import pango
import pangocairo
import numpy

import nmr
import singleton
import common


class Workspace(singleton.Singleton):
    """Beinhaltet alle Sachen, die den Workspace angehen: Verwaltung der Einstellungen... Und so halt."""
    oMain = None
    oSelf = None
    
    sFilepath = None
    bFileOpen = False
    oFile = None
    
    aWorkspaceData = {}
    
    pixmap = None
    loadedNMR = None
    
    # Statische Dinge
    iPickleProtocol = 2
    
    mouseTracking = False
    mouseLastRefresh = 0
    MOUSETRACKING_NONE   = 0
    MOUSETRACKING_SINGLE = 1
    MOUSETRACKING_DOUBLE = 2
    mouseStoreX = None
    
    # Statusbar
    statusBar_coordinates = None
    statusContext_coordinates = None
    statusMessage_coordinates = None
    statusBar_ppm = None
    statusContext_ppm = None
    statusMessage_ppm = None
    statusBar_i = None
    statusContext_i = None
    statusMessage_i = None
    
    b_spectra_controls_are_active = False
    
    o_sensitive_object = None
    temp_to_calibrate = None
    
    o_print_settings = None
    o_print_paper = None
    
    PRINT_WHOLE_SPECTRUM = 1
    PRINT_SIGNALS_ONLY = 2
    
    spectrumprintsettingsDialogErrorDict = {}
    
    def __init__(self, main):
        """ Initialisiert den Singleton-Pattern "Workspace" """
        singleton.Singleton.__init__(self)
        
        # Mutter-Objekt speichern
        self.oMain = main
        
    def clear(self):
        """ Bereitet das Objekt für ein erneutes Laden/Erstellen vor. """
        self.sFilepath = None
        self.aWorkspaceData = {}
        self.oFile = None
        self.oMain.addFilenameToTitle()
        
    def create(self, filepath, nmrpath):
        """ Erster Schritt der Dateientstehnung: Überprüft, obs den Pfad bereits gibt - wenn ja, 
        löscht er ihn und legt ihn in beiden Fällen neu an. Bei Verzeichnissen oder anderen Nicht-Dateien
        wird eine Exception ausgeworfen. """
        if os.path.exists(filepath):
            if os.path.isfile(filepath):
                # Datei löschen (gegengefragt haben wir ja)
                os.unlink(filepath)
                # Erstellung starten
                self.startCreation(filepath, nmrpath)
            else:
                raise Exception('Die angegebene Datei ist keine Datei. :)')
        else:
            self.startCreation(filepath, nmrpath)
            
    def startCreation(self, filepath, nmrpath):
        """ Unterfunktion für self.create, finalisiert die Dateierstellung, bzw beinhaltet den eigentlichen
        Schritt der Dateierstellung, in dem die Datei erstellt und mit ersten Daten gefüttert wird."""
        # Organisatorisches: Name in den Fenstertitel einfügen
        self.oMain.addFilenameToTitle(filepath)
        
        # Dateipfad (endlich) im Objekt speichern
        self.sFilepath = filepath
        
        # Datei öffnen
        self.oFile = open(self.sFilepath, 'wb')
        self.bFileOpen = True
        
        # Grundlegendes Workspace-Array kreieren:
        self.aWorkspaceData = {
            'nmrpath' : os.path.relpath(nmrpath, os.path.dirname(filepath)),
        }
        
        cPickle.dump(self.aWorkspaceData, self.oFile)
        self.oFile.close()
        self.oFile = open(self.sFilepath, 'r+b')
        
        # Bereite Workspace vor
        self.prepareWorkspace()
            
    def open(self, filepath):
        """ Open the file given by the argument. Close a already opened file.
        
        Keyword arguments:
        filpath -- the absolute path to the file
        
        """
        # Wenn bereits eine Datei offen ist, wird sie nun halt geschlossen und gespeichert
        if self.bFileOpen == True:
            self.close()
            self.bFileOpen = False
        
        if os.path.exists(filepath):
            self.startLoading(filepath)
        else:
            raise Exception('Die angegebene Datei existiert gar nicht. Kann da nix tun...')
            
    def startLoading(self, filepath):
        """ Start the loading procedure  itselt.
        
        Keyword arguments:
        filepath -- the absolut path to the file
        
        """
        self.oMain.addFilenameToTitle(filepath)
        self.sFilepath = filepath
        self.oFile = open(self.sFilepath, 'r+b')
        
        try:
            self.aWorkspaceData = cPickle.load(self.oFile)
            self.oFile.close()
            self.bFileOpen = True
        except EOFError:
            # Not a valid File =/
            print "Not a valid file"
            self.clear()
        
        # Bereite Workspace vor
        self.prepareWorkspace()
        
    def close(self):
        """ Close the opened file and save the workspace """
        if self.bFileOpen == True:
            self.oFile = open(self.sFilepath, 'wb')
            cPickle.dump(self.aWorkspaceData, self.oFile)
            self.oMain.builder.get_object("nmrOverviewStore").clear()
            # Dateizeiger schliessen
            self.oFile.close()
            # Objekt cleanen
            self.clear()
            
    def prepareWorkspace(self):
        """ Prepare the Workspace """
        self.oMain.builder.get_object("gNMR_workspace").set_sensitive(True)
        self.oMain.builder.get_object("mainwidgetscontainer_paned").set_sensitive(True)
        
        # Nach dem Laden sollten die NMRs aktualisiert werden.
        self.refreshNMRsCache()
        
    def activateSpectraControls(self):
        """ Activate the controls for the workspace. """
        if self.b_spectra_controls_are_active == False:
            self.oMain.builder.get_object("spectraBox").set_sensitive(True)
            self.oMain.builder.get_object("gNMR_spectra").set_sensitive(True)
            self.b_spectra_controls_are_active = True
        
    def deactivateSpectraControls(self):
        """ Deactivate the controls for the workspace. """
        if self.b_spectra_controls_are_active == True:
            self.oMain.builder.get_object("spectraToolbar").set_sensitive(False)
            self.oMain.builder.get_object("gNMR_spectra").set_sensitive(False)
            self.b_spectra_controls_are_active = False
        
    def refreshNMRsCache(self):
        """ Refresh the NMR cache. Walk trough nmrpath and compare its content with aWorkspaceData["nmrs"] """
        # Wenns den Unterpfad noch nicht gibt - erstellen.
        if "nmrs" not in self.aWorkspaceData:
            self.aWorkspaceData["nmrs"] = {}
        
        # Und los gehts
        dirs = os.listdir(os.path.join(os.path.dirname(self.sFilepath), self.getKey('nmrpath'), 'nmr'))
        for dir in dirs:
            if os.path.isdir(os.path.join(os.path.dirname(self.sFilepath), self.getKey('nmrpath'), 'nmr', dir)):
                self.refreshSingleNMRCache(dir)
                
        for nmrpar in self.aWorkspaceData["nmrs"]:
            piter = self.oMain.builder.get_object("nmrOverviewStore").append(None, [nmrpar, '', 'gtk-directory'])
            for nmrchi in self.aWorkspaceData["nmrs"][nmrpar]:
                self.oMain.builder.get_object("nmrOverviewStore").append(piter, [nmrchi, self.aWorkspaceData["nmrs"][nmrpar][nmrchi].getExperiment(), 'gtk-directory'])
                
    def refreshSingleNMRCache(self, dir):
        """ Refresh a NMR set by given path. Walks trough the directory and looks for subexperiments. """
        subdirs = os.listdir(os.path.join(os.path.dirname(self.sFilepath), self.getKey('nmrpath'), 'nmr', dir))
        for subdir in subdirs:
            if os.path.isdir(os.path.join(os.path.dirname(self.sFilepath), self.getKey('nmrpath'), 'nmr', dir, subdir)):
                # Hack, damit nur echte Verzeichnisse genommen werden
                # Benötigt überarbeitung und flexibilität!
                fidPath = os.path.join(os.path.dirname(self.sFilepath), self.getKey('nmrpath'), 'nmr', dir, subdir, 'fid')
                if os.path.exists(fidPath) and os.path.isfile(fidPath):
                    self.addSingleNMR(dir, subdir)
                else:
                    # Kein echtes Verzeichnis
                    pass
    
    def addSingleNMR(self, dir, subdir):
        """ Adds a single NMR to the list of nmrs. """
        #print "Adding NMR... %s, %s" % (dir, subdir)
        if dir not in self.aWorkspaceData["nmrs"]:
            # Elternverzeichnis in die Liste einfügen
            self.aWorkspaceData["nmrs"][dir] = {}
            #print "Mainpath added: %s" % (dir, )

        nmrpath = os.path.join(os.path.join(self.getKey('nmrpath'), 'nmr', dir, subdir))
        #self.aWorkspaceData["nmrs"][dir][subdir] = nmr.getNMR(os.path.join(os.path.dirname(self.sFilepath), nmrpath))
        self.aWorkspaceData["nmrs"][dir][subdir] = nmr.getNMR(nmrpath)
        
    def getNMRDir(self):
        return os.path.dirname(self.sFilepath)
        
    def setKey(self, key, value):
        """ Sets a key to a value
        
        Keyword arguments:
        key -- a string to identify the key
        value -- the value which the key should take
        
        """
        self.aWorkspaceData[key] = value
    
    def getKey(self, key):
        """ Get a value for a given key
        
        Keyword arguments:
        key -- a string to identify the key
        
        """
        if key in self.aWorkspaceData:
            return self.aWorkspaceData[key]
        else:
            return None
            
    def onRowActivated(self, treeview, path, treeviewColumn):
        """ Handle a activated nmr row from the TreeView """
        (model, row) = treeview.get_selection().get_selected()
        if model.iter_parent(row) is not None:
            info_chi = model.get(row, 0)[0]
            info_par = model.get(model.iter_parent(row), 0)[0]
            
            # Activate Spectra Controls
            self.activateSpectraControls()
            
            # Pixmal clearen.
            self.pixmap.clear()
            
            # NMR kopieren - wir wollen die Dateien nicht mit unnötigen Informationen füllen
            self.loadedNMR = copy.copy(self.aWorkspaceData["nmrs"][info_par][info_chi])
            self.loadedNMR.doFFT()
            self.loadedNMR.load()
                        
            self.pixmap.setNMR(self.loadedNMR)
            self.pixmap.setPlot(True)
            
    def configureDrawingArea(self, widget, event):
        """ Configure the drawing area """
        x, y, width, height = widget.get_allocation()
        
        if self.pixmap == None:
            self.pixmap = Workspace_plotArea(widget, width, height)
        else:
            self.pixmap.resize(widget, width, height)
        
        return True
        
    def exposeDrawingArea(self, widget, event):
        """ Expose (and redraw) the drawing area """
        x , y, width, height = event.area
        
        self.pixmap.refresh(x, y, width, height)
            
        return False
        
    def onActivateWidenTwice(self, widget):
        nmrArea = self.oMain.builder.get_object('nmrArea')
        size = nmrArea.get_size_request()
        nmrArea.set_size_request(size[0]*2, size[1])
        #self.pixmap.resize(width=size[0]/2, height=size[1])
        self.pixmap.refresh()
        
    def onActivateShortenTwice(self, widget):
        nmrArea = self.oMain.builder.get_object('nmrArea')
        size = nmrArea.get_size_request()
        nmrArea.set_size_request(size[0]/2, size[1])
        #self.pixmap.resize(width=size[0]/2, height=size[1])
        self.pixmap.refresh()
        
    def onNmrAreaMove(self, widget, event):
        """ Update the big cursor (currently only in x-direction).
        A dirty hack is used to decrease the frequency of the redraw to save cpu-time 
        since (at least on my computer) it seems very slow. Maybe this get a setting later 
        if I didn't find a nicer way
        Btw: 50 Hz is still to fast, 20 was to slow -- 33.33 seems to be okay.
        
        """
        x, y = event.get_coords()
        
        if self.mouseTracking == True:
            # Get the time!
            t = time.time()
            if t - self.mouseLastRefresh > 0.05:
                # If Δt > 0.05 (20 Hz), update!
                self.pixmap.setMousepos(x, y)
                self.mouseLastRefresh = t
            else:
                # Too early -- skip
                pass
        else:
            # No Tracking means: No "bar" to draw.
            # There is still need for tracking the mouse since the area should be mouse-sensitive and highlight certain parts..
            # Oh well, hope this doesn't slow down this too much...            
            self.pixmap.setMousepos(x, y, nobar = True)
            
        self.updateStatusbar(x, y)
        
        return False
        
    def on_nmrArea_leave(self, widget, event):
        #if self.mouseTracking == True:
            #self.pixmap.clearMouseLayer()
        #    #self.pixmap.refresh()
        #else:
        #    #self.pixmap.clearMousepos(nobar = True)
        #    
        #self.updateStatusbar()
        
        return False
        
    def updateStatusbar(self, x = None, y = None):
        """ Update the statusbar """
        # Reine x,y-Positionsbestimmung
        if self.statusBar_coordinates == None:
            self.statusBar_coordinates = self.oMain.builder.get_object('status_coordinates')
        if self.statusContext_coordinates == None:
            self.statusContext_coordinates = self.statusBar_coordinates.get_context_id('spectra_coordinates')
        if self.statusMessage_coordinates != None:
            self.statusBar_coordinates.remove(self.statusContext_coordinates, self.statusMessage_coordinates)
            
        if x == None or y == None:
            self.statusMessage_coordinates = self.statusBar_coordinates.push(self.statusContext_coordinates, 'Coordinates: (?, ?)')
        else:
            self.statusMessage_coordinates = self.statusBar_coordinates.push(self.statusContext_coordinates, 'Coordinates: (%s, %s)' % (x,y))
        
        # Umrechnung auf ppm
        if self.statusBar_ppm == None:
            self.statusBar_ppm = self.oMain.builder.get_object('status_ppm')
        if self.statusContext_ppm == None:
            self.statusContext_ppm = self.statusBar_ppm.get_context_id('spectra_coordinates')
        if self.statusMessage_ppm != None:
            self.statusBar_ppm.remove(self.statusContext_ppm, self.statusMessage_ppm)
        
        if x >= self.pixmap.padding and x < (self.pixmap.iWidth - self.pixmap.padding) and (x != None and y != None):
            self.statusMessage_ppm = self.statusBar_ppm.push(
                self.statusContext_ppm, 
                'PPM: %s (%s Hz)' % (
                    common.round_significant(self.pixmap.x2ppm(x), 5),
                    common.round_significant(self.pixmap.x2Hz(x), 5)
                )
            )
        else:
            self.statusMessage_ppm = self.statusBar_ppm.push(
                self.statusContext_ppm, 
                'PPM: ? (? Hz)'
            )
            
         # Umrechnung auf i...
        if self.statusBar_i == None:
            self.statusBar_i = self.oMain.builder.get_object('status_i')
        if self.statusContext_i == None:
            self.statusContext_i = self.statusBar_i.get_context_id('spectra_coordinates')
        if self.statusMessage_i != None:
            self.statusBar_i.remove(self.statusContext_i, self.statusMessage_i)
        
        if x >= self.pixmap.padding and x < (self.pixmap.iWidth - self.pixmap.padding) and (x != None and y != None):
            self.statusMessage_i = self.statusBar_i.push(self.statusContext_i, 'n: %s' % (int(round(self.pixmap.x2i(x), 0)), ))
        else:
            self.statusMessage_i = self.statusBar_i.push(self.statusContext_i, 'n: ?')
        
    def on_activatePeakPicking_toggled(self, widget):
        if widget.get_active() == True:
            self.mouseTracking = True
            self.mouseTrackingMode = self.MOUSETRACKING_SINGLE
            # Deaktiviere andere...
            self.oMain.builder.get_object('activateIntegrate').set_sensitive(False)
        else:
            self.mouseTracking = False
            self.mouseTrackingMode = self.MOUSETRACKING_NONE
            self.pixmap.clearMouseLayer()
            self.pixmap.refresh()
            # Deaktiviere andere...
            self.oMain.builder.get_object('activateIntegrate').set_sensitive(True)
            
    def on_activateIntegrate_toggled(self, widget):
        if widget.get_active() == True:
            self.mouseTracking = True
            self.mouseTrackingMode = self.MOUSETRACKING_DOUBLE
            # Deaktiviere andere...
            self.oMain.builder.get_object('activatePeakPicking').set_sensitive(False)
        else:
            self.mouseTracking = False
            self.mouseTrackingMode = self.MOUSETRACKING_NONE
            self.pixmap.clearMouseLayer()
            self.pixmap.refresh()
            # Deaktiviere andere...
            self.oMain.builder.get_object('activatePeakPicking').set_sensitive(True)
    
    def on_nmrArea_buttonPressed(self, widget, event):
        if self.mouseTracking == True:
            if self.mouseTrackingMode == self.MOUSETRACKING_SINGLE:
                # buttonPressed won't say which mouse button was pressed.
                # We have to ask buttonReleased which is much, much nicer to me.
                # I don't like you, buttonPressed. I'll pass! </3
                pass
            elif self.mouseTrackingMode == self.MOUSETRACKING_DOUBLE:
                self.mouseStoreX = event.x
                
    def on_nmrArea_buttonReleased(self, widget, event):
        # <3
        # Only if mouseTracking is On and the left mouse button is pressed.
        if self.mouseTracking == True and (event.get_state() & gtk.gdk.BUTTON1_MASK):
            # (Try to) Pick a single peak
            if self.mouseTrackingMode == self.MOUSETRACKING_SINGLE:
                # Get the number behind the "pixel"
                i = self.pixmap.x2i(event.x)
                # Get the (lowest) number behind pixel-1 and the 
                #   (highest) number behind pixal+1 to define a good 
                #   "sharpness"
                il = self.pixmap.x2i(event.x-2) # i_lower
                iu = self.pixmap.x2i(event.x+2) # i_upper
                sharpness = iu - il
                
                # Search that peak!
                peaks = self.loadedNMR.searchPeak(i, sharpness, threshold=3*self.loadedNMR.y_values_sino)
                if peaks != (0, 0):
                    print "Found Peak %s (3xSINO: %s; s=%s) " % (peaks, 3*self.loadedNMR.y_values_sino, sharpness)
                    self.loadedNMR.addPeak(peaks[0])
                    # Redraw Plot
                    self.pixmap.redrawPeakLayer()
                else:
                    print "No peak found!"
            elif self.mouseTrackingMode == self.MOUSETRACKING_DOUBLE and self.mouseStoreX != None:
                xStart = self.mouseStoreX
                xEnd = event.x
                self.mouseStoreX = None
                
                # xStart should be left (smaller), xEnd right (bigger x!)
                # Therefore: If its vice-versa, we change start and the end
                if xStart > xEnd:
                    t = xStart
                    xStart = xEnd
                    xEnd = t
                
                # A difference of only 2 pixels is probably only a wrong click.
                # Maybe a setting for this would be cool...
                if xEnd - xStart > 2:
                    i_n_l = self.pixmap.x2i(xStart)
                    i_n_r = self.pixmap.x2i(xEnd) - 1
                    self.loadedNMR.addIntegral(i_n_l, i_n_r)
                    self.pixmap.redrawIntegralLayer()
                else:
                    print "xEnd - xStart <= 2: I don't know what to do!"
        elif event.get_state() & gtk.gdk.BUTTON3_MASK:            
            self.pixmap.showContextMenu(event = event, builder = self.oMain.builder)
            
    def onActivateSpectrumSave(self, widget):
        self.loadedNMR.save()
        
    def on_contextDeleteIntegral_activate(self, widget):
        sensitive_object = self.pixmap.getSensitiveObject()
        
        if isinstance(sensitive_object, nmr.integrals.Integral):
            self.loadedNMR.deleteIntegral(integral = sensitive_object)
            self.pixmap.clearSensitiveArea()
            self.pixmap.redrawIntegralLayer()
            
    def on_contextDeletePeak_activate(self, widget):
        sensitive_object = self.pixmap.getSensitiveObject()
        
        if isinstance(sensitive_object, nmr.peaks.Peak):
            self.loadedNMR.deletePeak(peak = sensitive_object)
            self.pixmap.clearSensitiveArea()
            self.pixmap.redrawPeakLayer()
            
    def on_contextCalibrateIntegral_activate(self, widget):
        sensitive_object = self.pixmap.getSensitiveObject()
        
        if isinstance(sensitive_object, nmr.integrals.Integral):
            # Save integral in workspace, too - just in case it get lost.
            self.o_sensitive_object = sensitive_object
            
            self.oMain.builder.get_object('calibrateDialog_form_calibrateInput').set_text(str(self.o_sensitive_object.getArea()))
            self.oMain.o_dialogs["calibrate"].run()
            
    def on_calibrateDialog_close(self, widget, b = None):
        self.temp_to_calibrate = None
        self.oMain.builder.get_object('calibrateDialog_form_calibrateInput').set_text("")
        self.oMain.o_dialogs["calibrate"].hide()
        
    def on_calibrateDialog_form_calibrateInput_changed(self, e):
        text = e.get_text()
        if text != "":
            try:
                if text[-1] == '.':
                    fl = numpy.float(text[0:-1])
                else:
                    fl = numpy.float(text)
                    
                if round(fl, 2) == 0.00:
                    self.oMain.builder.get_object('calibrateDialogOK').set_sensitive(False)
                else:
                    self.oMain.builder.get_object('calibrateDialogOK').set_sensitive(True)
                    self.temp_to_calibrate = fl
            except ValueError:
                self.oMain.builder.get_object('calibrateDialogOK').set_sensitive(False)
        else:
            self.oMain.builder.get_object('calibrateDialogOK').set_sensitive(False)
        
        return False
        
    def on_calibrateDialog_commit(self, widget):
        # Okay... We now have a commit. That means...
        if self.temp_to_calibrate != None:
            # Yeah - calibration is okay. Now...
            calibrationValue = self.o_sensitive_object.getAbsArea() / self.temp_to_calibrate
            self.loadedNMR.setIntegralReference(calibrationValue)
            
            # Set dialog back to 0
            self.oMain.builder.get_object('calibrateDialog_form_calibrateInput').set_text("")
            self.oMain.o_dialogs["calibrate"].hide()
            self.temp_to_calibrate = None
            self.o_sensitive_object = None
    
    def on_menu_spectrum_printSpectrum_activate(self, widget):
        print_op = gtk.PrintOperation()
        
        # Set current page to 0 - there will be only one page to print. I guess.
        print_op.set_n_pages(1)
        print_op.set_current_page(0)
        print_op.set_support_selection(False)
        print_op.set_job_name('gLabs NMR Plot')
        
        # Set asynchronous printing to True
        # print_op.set_allow_async(True)
        
        if self.o_print_settings != None:
            print_op.set_print_settings(self.o_print_settings)
        else:
            settings = gtk.PrintSettings()
            settings.set_paper_size(gtk.PaperSize('iso_a4_210x297mm'))
            settings.set_orientation(gtk.PAGE_ORIENTATION_LANDSCAPE)
            settings.set_resolution_xy(300, 300) # Doesn’t work
            print_op.set_print_settings(settings)
            
        if self.o_print_paper != None:
            print_op.set_default_page_setup(self.o_print_paper)
        else:
            paper = gtk.PageSetup()
            paper.set_paper_size(gtk.PaperSize('iso_a4_210x297mm'))
            paper.set_orientation(gtk.PAGE_ORIENTATION_LANDSCAPE)
            print_op.set_default_page_setup(paper)
            
        print_op.connect('begin_print', self.on_begin_print)
        print_op.connect('draw_page', self.on_draw_page, self.PRINT_WHOLE_SPECTRUM)
        
        res = print_op.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, self.oMain.mainWindow)
        
        if res == gtk.PRINT_OPERATION_RESULT_APPLY:
            self.o_print_settings = print_op.get_print_settings()
            
    def on_begin_print(self, operation, context):
        print "BEGIN PRINT (stub)"
        
    def on_draw_page(self, operation, context, page, what):
        print "DRAW PAGE"
        if what == self.PRINT_WHOLE_SPECTRUM:
            cairo_context = context.get_cairo_context()
            print "Print... DPI: %s×%s" % (context.get_dpi_x(), context.get_dpi_y())
            self.pixmap.getPrintToContext(cairo_context, width = context.get_width(), height = context.get_height())

    def on_menu_spectrum_printPageSettings_activate(self, widget):
        if self.o_print_settings == None:
            self.o_print_settings = gtk.PrintSettings()
            
        if self.o_print_paper == None:
            self.o_print_paper = gtk.PageSetup()
            self.o_print_paper.set_paper_size(gtk.PaperSize('iso_a4_210x297mm'))
            self.o_print_paper.set_orientation(gtk.PAGE_ORIENTATION_LANDSCAPE)
            
        self.o_print_paper = gtk.print_run_page_setup_dialog(self.oMain.mainWindow, self.o_print_paper, self.o_print_settings)
        
    def on_menu_spectrum_printSpectrumSettings_activate(self, widget):
        self.oMain.builder.get_object('leftborder_input').set_text(str(round(self.loadedNMR.getCustomLeftBorder(), 5)))
        self.oMain.builder.get_object('rightborder_input').set_text(str(round(self.loadedNMR.getCustomRightBorder(), 5)))  
        self.reset_spectrumprintsettingsDialog_form_input_dict()
              
        self.oMain.o_dialogs["spectrumprintsettings"].run()
        
    def on_spectrumprintsettingsDialog_close(self, widget, b = None):
        self.oMain.builder.get_object('leftborder_input').set_text("")
        self.oMain.builder.get_object('rightborder_input').set_text("")
        self.reset_spectrumprintsettingsDialog_form_input_dict()
        
        self.oMain.o_dialogs["spectrumprintsettings"].hide()
        
    def on_spectrumprintsettingsDialog_commit(self, widget):
        leftborder = numpy.float(self.oMain.builder.get_object('leftborder_input').get_text())
        rightborder = numpy.float(self.oMain.builder.get_object('rightborder_input').get_text())
        
        if leftborder < rightborder:
            t = rightborder
            rightborder = leftborder
            leftborder = t
        
        # Set variables to the spectra
        self.loadedNMR.setCustomLeftBorder(leftborder)
        self.loadedNMR.setCustomRightBorder(rightborder)
        
        # Clean-Up
        self.on_spectrumprintsettingsDialog_close(widget)
        
    def reset_spectrumprintsettingsDialog_form_input_dict(self):
        self.spectrumprintsettingsDialogErrorDict = {
            'leftborder' : 0,
            'rightborder' : 0,
        }
        
    def on_spectrumprintsettingsDialog_form_input_changed(self, e):
        text = e.get_text()
        spectrumprintsettingsDialogErrors = 0
        
        # Left border
        if e == self.oMain.builder.get_object('leftborder_input'):
            label = self.oMain.builder.get_object('leftborder_label')
            
            if common.is_numeric(text):
                self.spectrumprintsettingsDialogErrorDict['leftborder'] = 0
                label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('black'))
            else:
                self.spectrumprintsettingsDialogErrorDict['leftborder'] = 1
                label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('red'))
                
        # Right border
        elif e == self.oMain.builder.get_object('rightborder_input'):
            label = self.oMain.builder.get_object('rightborder_label')
            
            if common.is_numeric(text):
                self.spectrumprintsettingsDialogErrorDict['rightborder'] = 0
                label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('black'))
            else:
                self.spectrumprintsettingsDialogErrorDict['rightborder'] = 1
                label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('red'))
                
        for i in self.spectrumprintsettingsDialogErrorDict:
            spectrumprintsettingsDialogErrors += self.spectrumprintsettingsDialogErrorDict[i]
            
        if spectrumprintsettingsDialogErrors > 0:
            self.oMain.builder.get_object('spectrumprintsettingsDialogOK').set_sensitive(False)
        else:
            self.oMain.builder.get_object('spectrumprintsettingsDialogOK').set_sensitive(True)
            
        


class Workspace_plotArea(object):
    """ Funktioniert. """
    data = {
        'x_val' : [],
        'y_val' : [],
    }
    
    # Fenster-Parameter
    iWidth = 0
    iHeight = 0
    
    oWidget = None
    oNMR = None
    oCairo = None
    pangoFont = None
    pangoFontHighlight = None
    pangoFontPrint = None
    
    doPlot = False
    
    padding = 0
    x_axis_pos = 90
    spectra_space_bottom = 20
    spectra_space_top = 100
    
    i_peak_stroke_length = 10
    i_peak_stroke_head = 3
    i_peak_stroke_foot = 3
    i_peak_space_to_spectra = 10
    i_peak_space_to_label = 10
    
    i_integral_space_to_baseline = 15
    i_integral_space_to_top = 20
    i_integral_stroke_length = 5
    i_integral_stroke_head = 5
    i_integral_stroke_foot = 60
    i_integral_space_to_x_axis = 10
    
    x_axis_labels = 20
    x_axis_sublabels = 10 # Num of steps _with_ the start. 10 means there are 9 additional marks between two main labels
    
    cairoLinewidth = 0.75
    f_cairo_linewidth_highlight = 2
    f_cairo_printlinewidth = 0.1
    
    # Mouse position
    mousePosX = -1       # Mouse X position
    mousePosY = -1       # Mouse Y position
    mouseLayer = None    # Cairo surface object for the mouse layer
    mouseLayerCon = None # Context of mouse layer
    mouseMode = 1        # 1 = Whole Layer, 2 = Layer with 1 pixel width.. (Only 1 does work. Yet?)
    mouse_area = None
    
    MOUSE_AREA_NONE = None
    MOUSE_AREA_PEAK = 1
    MOUSE_AREA_INTEGRAL = 2
    
    # Layers
    o_layer_peaks = [None, None]
    o_layer_integrals = [None, None]
    o_pango_fontmap = None
    o_pango_contexts = [None, None]
    
    # Sensitive Areas
    l_sensitive_peak_area = {}
    l_sensitive_integral_area = {}
    o_sensitive_object = None
    
    STANDARD_HEIGHT = 1000
    STANDARD_WIDTH = 2000
    
    # Temp...
    i_peaks_pixelshift = 0
    
    print_temp = {}
    
    def __init__(self, widget, width, height):
        self.iWidth = width
        self.iHeight = height
        self.iWidthDraw = width
        self.iHeightDraw = height
        self.oWidget = widget
        
        # Standad-Schriftart festlegen
        self.pangoFont = pango.FontDescription('Sans 9')
        self.pangoFontHighlight = pango.FontDescription('Sans Bold 9')
        self.pangoFontPrint = pango.FontDescription('Sans 10')
        
        # Standard-Fontmap festlegen
        self.o_pango_fontmap = pangocairo.cairo_font_map_get_default()
        
        # Context 1: Achse
        self.o_pango_contexts[0] = self.o_pango_fontmap.create_context()
        self.o_pango_contexts[0].load_font(self.pangoFont)
        
        # Plot-Layer (Plot + Achse) initialisieren
        self.initPlotLayer()
        
        # Peak-Layer (Peak-Strichel, Zahlen) initialisieren
        self.initPeakLayer()
        
        # Integral-Layer
        self.initIntegralLayer()
        
    def initPlotLayer(self):
        self.oCairoOff = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.iWidth, self.iHeight)
        self.oCairoOffC = cairo.Context(self.oCairoOff)
        # PangoCairo-Context drüber hauen
        self.oCairoOffC = pangocairo.CairoContext(self.oCairoOffC)
        
        # Einstellungen
        self.oCairoOffC.set_line_width(self.cairoLinewidth)
        
        # Weisse Fläche initialisieren
        self.oCairoOffC.set_source_rgb(1.0, 1.0, 1.0)
        self.oCairoOffC.rectangle(0, 0, self.iWidth, self.iHeight)
        self.oCairoOffC.fill()
        
    def initPeakLayer(self):
        self.o_layer_peaks[0] = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.iWidth, self.iHeight)
        self.o_layer_peaks[1] = cairo.Context(self.o_layer_peaks[0])
        self.o_layer_peaks[1] = pangocairo.CairoContext(self.o_layer_peaks[1])
        
        self.o_layer_peaks[1].set_line_width(self.cairoLinewidth)
        
        self.initSensitivePeakArea()
        
    def initSensitivePeakArea(self):
         self.l_sensitive_peak_area = {}
         
    def initIntegralLayer(self):
        self.o_layer_integrals[0] = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.iWidth, self.iHeight)
        self.o_layer_integrals[1] = cairo.Context(self.o_layer_integrals[0])
        self.o_layer_integrals[1] = pangocairo.CairoContext(self.o_layer_integrals[1])
        
        self.o_layer_integrals[1].set_line_width(self.cairoLinewidth)
        
        self.initSensitiveIntegralArea()
       
    def initSensitiveIntegralArea(self):
        self.l_sensitive_integral_area = {}
        
    def resize(self, widget = None, width = 0, height = 0):
        self.iWidth = width
        self.iHeight = height
        self.iWidthDraw = width
        self.iHeightDraw = height
        
        self.initPlotLayer()
        self.initPeakLayer()
        self.initIntegralLayer()
        
        if self.doPlot:
            # Redraw Layers
            self.plot()
            self.drawPeakLayer()
            self.drawIntegralLayer()
            
            # Refresh drawing area
            self.refresh()
        
    def setNMR(self, nmr):
        self.oNMR = nmr
        
    def setPlot(self, what):
        self.doPlot = what
        if what == True:
            self.plot()
            self.drawPeakLayer()
            self.drawIntegralLayer()
            self.refresh()
        else:
            # Fläche löschen.
            self.oCairoOffC.set_source_rgb(1.0, 1.0, 1.0)
            self.oCairoOffC.rectangle(0, 0, self.iWidth, self.iHeight)
            self.oCairoOffC.fill()
            self.oCairoOffC.set_antialias(False)
            
            # Re-Init anderer Flächen
            self.initPeakLayer()
            self.initIntegralLayer()
        
    def refresh(self, x = 0, y = 0, width = None, height = None):
        """ Refresh the drawing Area """
        if width == None:
            width = self.iWidthDraw
        
        if height == None:
            height = self.iHeightDraw
            
        # Cairo initialisieren
        self.oCairo = self.oWidget.window.cairo_create()
        
        # Einstellungen
        self.oCairo.set_line_width(self.cairoLinewidth)
        self.oCairo.set_antialias(False)
        
        if self.doPlot == True and isinstance(self.oNMR, nmr.nmr_prototype.NMR):
            # NMR-Plot drüber klatschen
            self.addPlotLayer()
            
            # Peak-Layer
            self.addPeakLayer()
            
            # Integral-Layer
            self.addIntegralLayer()
            
            # Mouse-Layer
            if self.mouseLayerCon:
                if self.mouseMode == 1:
                    self.oCairo.set_source_surface(self.mouseLayer, 0, 0)
                    self.oCairo.rectangle(0, 0, self.iWidthDraw, self.iHeightDraw)
                    self.oCairo.fill()
                elif self.mouseMode == 2:
                    self.oCairo.set_source_surface(self.mouseLayer, 0, 0)
                    self.oCairo.rectangle(self.x(self.mousePosX, True, ignorePadding = True), 0, 1, self.iHeightDraw)
                    self.oCairo.fill()
                    
    def addPlotLayer(self):
        self.oCairo.set_source_surface(self.oCairoOff, 0, 0)
        self.oCairo.rectangle(0, 0, self.iWidthDraw, self.iHeightDraw)
        self.oCairo.fill()
                    
    def addPeakLayer(self):
        self.oCairo.set_source_surface(self.o_layer_peaks[0], 0, 0)
        self.oCairo.rectangle(0, 0, self.iWidthDraw, self.iHeightDraw)
        self.oCairo.fill()
        
    def addIntegralLayer(self):
        self.oCairo.set_source_surface(self.o_layer_integrals[0], 0, 0)
        self.oCairo.rectangle(0, 0, self.iWidthDraw, self.iHeightDraw)
        self.oCairo.fill()
        
    def clear(self):
        self.clearMousepos()
        self.clearSensitiveArea()
        
        self.setPlot(False)
        self.data = {
            'x_val' : [],
            'y_val' : [],
        }
        self.oNMR = None
        
    def clearSensitiveArea(self):
        self.initSensitivePeakArea()
        self.initSensitiveIntegralArea()
        self.o_sensitive_object = None
           
    def drawPeakLayer(self, context = None):
        if context == None:
            context = self.o_layer_peaks[1]
            c = True
            width = self.iWidth
        else:
            c = False
            width = self.print_temp['width']
        
        self.i_peaks_pixelshift = 0
        
        # Get those peaks
        peaks = self.oNMR.getPeaks()
        
        if peaks != None:
            for peak in peaks:
                if c == True:
                    if self.mouse_area == self.MOUSE_AREA_PEAK and self.o_sensitive_object == peak and c == True:
                        context.set_line_width(self.f_cairo_linewidth_highlight)
                    else:
                        context.set_line_width(self.cairoLinewidth)
                               
                context.save()
                
                # Gebe das Layout
                layout = context.create_layout()
                
                # Schreibe..
                if c == True:
                    layout.set_font_description(self.pangoFontHighlight if (self.mouse_area == self.MOUSE_AREA_PEAK and self.o_sensitive_object == peak) else self.pangoFont)
                else:
                    layout.set_font_description(self.pangoFontPrint)
                layout.set_alignment(pango.ALIGN_LEFT)
                text = str(peak.getPPM(True, 5)).ljust(6, '0')
                layout.set_text(text)
                
                size = layout.get_pixel_size()
                
                x = self.ppm2x(peak.getPPM()) - math.ceil(size[1]/2)
                
                if self.ppm2x(peak.getPPM()) + math.ceil(size[1]/2) > self.i_peaks_pixelshift and self.i_peaks_pixelshift != 0:
                    x = self.i_peaks_pixelshift - math.ceil(size[1]/2)*1.5
                
                self.i_peaks_pixelshift = x
                
                x_l = x
                x_r = x + size[1]
                
                if x_l < 0 or x_l > width or x_r < 0 or x_l > width:
                    continue
                
                context.set_source_rgb(0.0, 0.5, 0.0)
                context.move_to(
                    self.x(x), 
                    self.y(self.spectra_space_top-self.i_peak_space_to_spectra-self.i_peak_stroke_length-self.i_peak_space_to_label, True)
                )
                
                context.rotate(-math.pi/2)
                context.show_layout(layout)
                context.restore()
                
                # Draw peak-Stroke
                context.set_source_rgb(0.0, 0.5, 0.0)
                context.move_to(
                    self.x(self.ppm2x(peak.getPPM())), 
                    self.y(self.spectra_space_top-self.i_peak_space_to_spectra, True)
                )
                context.line_to(
                    self.x(self.ppm2x(peak.getPPM())), 
                    self.y(self.spectra_space_top-self.i_peak_space_to_spectra-self.i_peak_stroke_foot, True)
                )
                context.line_to(
                    self.x(x + math.ceil(size[1]/2)), 
                    self.y(self.spectra_space_top-self.i_peak_space_to_spectra-self.i_peak_stroke_foot-self.i_peak_stroke_length, True)
                )
                context.line_to(
                    self.x(x + math.ceil(size[1]/2)), 
                    self.y(self.spectra_space_top-self.i_peak_space_to_spectra-self.i_peak_stroke_foot-self.i_peak_stroke_length-self.i_peak_stroke_head, True)
                )
                context.stroke()
                
                # Calibrate sensitive area
                if c == True:
                    for a in range(int(round(x_l, 0)), int(round(x_r+1, 0))):
                        self.l_sensitive_peak_area[a] = peak
                
    def redrawPeakLayer(self, refresh = True):
        self.initPeakLayer()
        self.drawPeakLayer()
        
        # Refresh drawing area
        if refresh == True:
            self.refresh()
    
    def drawIntegralLayer(self, context = None):
        if context == None:
            context = self.o_layer_integrals[1]
            c = True
            width = self.iWidth
        else:
            c = False
            width = self.print_temp['width']
            
        integrals = self.oNMR.getIntegrals()
        
        if integrals != None:            
            # Define color
            context.set_source_rgb(1, 0.0, 0.0)
            
            # First round: Calculate this fucking height..
            y_sum_max = 0
            
            for integral in integrals:
                # Get maximal area
                y_sum = integral.getAbsArea()
                if y_sum > y_sum_max:
                    y_sum_max = y_sum
                    
            if y_sum_max == 0:
                y_sum_max = 1
                
            # Calculate zoom
            if c == True:
                int_zoom = (self.spectraHeight - self.i_integral_space_to_top) / y_sum_max
            else:
                int_zoom = (self.print_temp['spectrum_height'] - self.i_integral_space_to_top) / y_sum_max
            
            # Second round: Draw Integral(s)!
            for integral in integrals:
                first = True
                x_pos_l = integral.getStart()
                x_pos_r = integral.getEnd()
                
                if self.ppm2x(self.oNMR.i2ppm(x_pos_l)) < 0 or self.ppm2x(self.oNMR.i2ppm(x_pos_l)) > width:
                    continue
                
                # Set bold area if the integral is hovered
                if c == True:
                    if self.mouse_area == self.MOUSE_AREA_INTEGRAL and self.o_sensitive_object == integral:
                        context.set_line_width(self.f_cairo_linewidth_highlight)
                    else:
                        context.set_line_width(self.cairoLinewidth)
                else:
                    context.set_line_width(self.f_cairo_printlinewidth)
            
                # Get range
                integral_points = range(x_pos_l, x_pos_r+1)
                
                # Predefine Integral-High
                y = self.spectra_space_bottom + self.i_integral_space_to_baseline + self.x_axis_pos
                
                # Prepare to Draw the Integral-Line
                for integral_point in integral_points:
                    if first == True:                        
                        # Move painting-cursor to the start point
                        context.move_to(
                            self.x(self.ppm2x(self.oNMR.i2ppm(integral_point))), 
                            self.y(y)
                        )
                        
                        y = y + self.oNMR.n2height(integral_point)*int_zoom
                        
                        context.line_to(
                            self.x(self.ppm2x(self.oNMR.i2ppm(integral_point))), 
                            self.y(y)
                        )
                    else:
                        y = y + self.oNMR.n2height(integral_point)*int_zoom
                       
                        context.line_to(
                            self.x(self.ppm2x(self.oNMR.i2ppm(integral_point))), 
                            self.y(y)
                        )
                    
                    first = False
                    
                # Prepare to Draw the Integral-Information on the bottom of the spectra
                x_l = self.ppm2x(self.oNMR.i2ppm(x_pos_l))
                x_r = self.ppm2x(self.oNMR.i2ppm(x_pos_r))
                integral_width = abs(x_r - x_l)
                integral_middle = x_l + integral_width/2
                
                # Write the peak-value.
                context.save()
                
                # Give the layout
                layout = context.create_layout()
                
                # Write..
                if c == True:
                    layout.set_font_description((self.pangoFontHighlight if (self.mouse_area == self.MOUSE_AREA_INTEGRAL and self.o_sensitive_object == integral) else self.pangoFont))
                else:
                    layout.set_font_description(self.pangoFontPrint)
                
                layout.set_alignment(pango.ALIGN_LEFT)
                text = str(common.round_significant(integral.getArea(), 5)).ljust(6, '0')
                layout.set_text(text)
                
                size = layout.get_pixel_size()
                
                x = integral_middle - math.ceil(size[1]/2)
                
                #if self.i2x(peak.getN()) + math.ceil(size[1]/2) > self.i_peaks_pixelshift and self.i_peaks_pixelshift != 0:
                #    x = self.i_peaks_pixelshift - math.ceil(size[1]/2)*1.5
                #self.i_peaks_pixelshift = x
                
                context.move_to(
                    self.x(x), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head - self.i_integral_stroke_length - self.i_integral_stroke_foot)
                )
                
                context.rotate(-math.pi/2)
                context.show_layout(layout)
                context.restore()
                
                integral_caption_width = size[1] # Have to calc that one later!
                x_l_2 = integral_middle - integral_caption_width/2
                x_r_2 = integral_middle + integral_caption_width/2
                
                # Left side
                context.move_to(
                    self.x(x_l),
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis)
                )
                context.line_to(
                    self.x(x_l), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head)
                )
                context.line_to(
                    self.x(x_l_2), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head - self.i_integral_stroke_length)
                )
                context.line_to(
                    self.x(x_l_2), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head - self.i_integral_stroke_length - self.i_integral_stroke_foot)
                )
                
                # Right side
                context.move_to(
                    self.x(x_r),
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis)
                )
                context.line_to(
                    self.x(x_r), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head)
                )
                context.line_to(
                    self.x(x_r_2), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head - self.i_integral_stroke_length)
                )
                context.line_to(
                    self.x(x_r_2), 
                    self.y(self.x_axis_pos - self.i_integral_space_to_x_axis - self.i_integral_stroke_head - self.i_integral_stroke_length - self.i_integral_stroke_foot)
                )
                    
                # Draw!
                context.stroke()
                
                # Calibrate sensitive area
                if c == True:
                    for a in range(int(round(x_l, 0)), int(round(x_r+1, 0))):
                        self.l_sensitive_integral_area[a] = integral
        
    def redrawIntegralLayer(self, refresh = True):
        self.initIntegralLayer()
        self.drawIntegralLayer()
        
        # Refresh drawing area
        if refresh == True:
            self.refresh()
    
    def plot(self, context = None):
        if context == None:
            values = self.oNMR.getValues()
            c = True
        else:
            values = self.oNMR.getCustomSpectrum()
            c = False
        
        self.data['y_val'] = values[0]
        if len(values) == 2:
            self.data['x_val'] = values[1]
        else:
            self.data['x_val'] = range(0, len(self.data['y_val']))
            
        # Draw x axis
        self.drawXAxis(context = context)
        
        if c == True:
            # Berechne die Maximal-Höhe für das Zeichnen
            self.spectraHeight = self.iHeight - 2*self.padding - self.x_axis_pos - self.spectra_space_bottom - self.spectra_space_top
            self.spectraWidth = self.iWidth - 2*self.padding
            
            if self.spectraHeight > 0:
                # Nur zeichnen, wenn überhaupt Platz vorhanden ist.
                # Suche Maximum und Minimum für y-Werte
                if isinstance(self.data['y_val'], dict) == False:
                    y_min = self.data['y_val'].min()
                    y_max = self.data['y_val'].max()
                else:
                    y_min = min(self.data['y_val'])
                    y_max = max(self.data['y_val'])
                
                # Berechne Differenz
                y_dif = y_max - y_min
                
                # Das selbe für x:
                x_dif = len(self.data['y_val'])
                
                # Berechne das Mapping Pixel => y
                self.zoom_y = float(self.spectraHeight) / y_dif
                self.zoom_x = float(self.spectraWidth) / x_dif
                
                # Zeichne Plot
                self.drawPlot()
                self.flagXAxis()
        else:
            self.print_temp['spectrum_height'] = self.print_temp['height'] - 2*self.padding - self.x_axis_pos - self.spectra_space_bottom - self.spectra_space_top
            self.print_temp['spectrum_width'] = self.print_temp['width'] - 2*self.padding
            
            if self.print_temp['spectrum_height'] > 0:
                # Nur zeichnen, wenn überhaupt Platz vorhanden ist.
                # Suche Maximum und Minimum für y-Werte
                if isinstance(self.data['y_val'], dict) == False:
                    y_min = self.data['y_val'].min()
                    y_max = self.data['y_val'].max()
                else:
                    y_min = min(self.data['y_val'])
                    y_max = max(self.data['y_val'])

                # Berechne Differenz
                y_dif = y_max - y_min

                # Das selbe für x:
                x_dif = len(self.data['y_val'])

                # Berechne das Mapping Pixel => y
                self.print_temp['zoom_y'] = float(self.print_temp['spectrum_height']) / y_dif
                self.print_temp['zoom_x'] = float(self.print_temp['spectrum_width']) / x_dif

                # Zeichne Plot
                self.drawPlot(context = context)
                self.flagXAxis(context = context)
        
    def drawXAxis(self, context = None):
        # Zeichnet die Achsen-Linie
        if context == None:
            context = self.oCairoOffC
        
        context.set_source_rgb(0.0, 0.0, 0.0)
        context.move_to(self.x(0), self.y(self.x_axis_pos))
        context.line_to(self.x(0, True), self.y(self.x_axis_pos))
        context.stroke()
      
    def flagXAxis(self, context = None):
        if context == None:
            context = self.oCairoOffC
            c = True
            width = self.iWidth
            specWidth = self.oNMR.getSpectralWidth()
            specLeftPPM = self.oNMR.getOffset()
            specRightPPM = specLeftPPM - specWidth
        else:
            c = False
            width = self.print_temp['width']
            specWidth = self.oNMR.getCustomSpectrumWidth()
            specLeftPPM = self.oNMR.getCustomLeftBorder()
            specRightPPM = self.oNMR.getCustomRightBorder()
        
        resolution = len(self.data['y_val']) / specWidth
        diffPPM = specWidth - specLeftPPM
        
        #print "Flag X-Axis: Width: %s / %s = %s (%s)" % (specWidth, self.x_axis_labels, math.floor(specWidth / self.x_axis_labels), specWidth / self.x_axis_labels)
        
        steps = round(specWidth / self.x_axis_labels * 2, 0) / 2
        
        if steps == 0:
            steps = 0.1
        
        #if math.ceil(specWidth / self.x_axis_labels) < 1:
        #    steps = math.ceil(specWidth)
        #else:
        #    steps = math.ceil(specWidth / self.x_axis_labels)
        #print "numpy.arange: %s, %s (%s), %s" % (0, specLeftPPM + steps, specLeftPPM, steps)
        #print "numpy.arange: %s, %s (%s), %s" % (0, specRightPPM - steps, specRightPPM, steps)
        
        if specRightPPM < 0 and specLeftPPM > 0:
            start = 0
        else:
            start = round(round(specWidth) / 2, 0)
        
        rangeUpwards = numpy.arange(start, specLeftPPM + 1, steps).tolist()
        rangeUpwards.sort()
        rangeDownwards = numpy.arange(start, specRightPPM - 1, -1*steps).tolist()
        rangeDownwards.sort()
        if len(rangeDownwards) > 0:
            rangeDownwards.pop() # Remove start
        rangeDownwards.extend(rangeUpwards)
        
        if steps % 1 == 0:
            rangeDownwards = map(int, rangeDownwards)
        
        specLeftPPMMark = math.ceil(specLeftPPM) if specLeftPPM < 0 else math.floor(specLeftPPM)
        specRightPPMMark = math.ceil(specRightPPM) if specRightPPM < 0 else math.floor(specRightPPM)
        
        print "Flag X-Axis: range(%s, %s, %s)" % (specLeftPPMMark, specRightPPMMark, -1*steps)
        
        for i in rangeDownwards:
            context.set_source_rgb(0.0, 0.0, 0.0)
            
            iLess = i - steps
            
            if c == True:
                x = (len(self.data['y_val']) - ((i + diffPPM) * resolution)) * self.zoom_x
                xLess = (len(self.data['y_val']) - ((iLess + diffPPM) * resolution)) * self.zoom_x
            else:
                x = (len(self.data['y_val']) - ((i + diffPPM) * resolution)) * self.print_temp['zoom_x']
                xLess = (len(self.data['y_val']) - ((iLess + diffPPM) * resolution)) * self.print_temp['zoom_x']
            
            xDiff = xLess - x
            j = 0
            jMax = int(self.x_axis_sublabels / 2)
            jMin = int(self.x_axis_sublabels / -2)
            
            for j in range(jMin, jMax):
                x2 = x + xDiff * (j+1)/(jMax*2)
                if x2 > 0 and x2 < width:
                    context.move_to(self.x(x2), self.y(self.x_axis_pos + 0))
                    context.line_to(self.x(x2), self.y(self.x_axis_pos - 4))
                    
            if x > 0 and x < width and x+20 > 0 and x+20 < width:
                #context.set_source_rgb(0.0, 0.0, 0.0)
                context.move_to(self.x(x), self.y(self.x_axis_pos + 4))
                context.line_to(self.x(x), self.y(self.x_axis_pos - 4))
                context.stroke()
                
                layout = context.create_layout()
                layout.set_font_description(self.pangoFont if c == True else self.pangoFontPrint)
                layout.set_alignment(pango.ALIGN_CENTER)
                layout.set_width(20)
                layout.set_text(str(i))
                
                context.set_source_rgb(0.0, 0.0, 0.0)
                context.move_to(self.x(x), self.y(self.x_axis_pos - 5))
                context.show_layout(layout)
            else:
                context.stroke()
        
    def drawPlot(self, context = None):
        if context == None:
            context = self.oCairoOffC
            c = True
        else:
            c = False
            
        # Lege mit dem Polygon los.
        points = []
        # Schwarzer Strich
        context.set_source_rgb(0.0, 0.0, 0.0)
        # Startpunkt setzen
        context.move_to(self.x(0), self.y(self.x_axis_pos + self.spectra_space_bottom))
        
        x = 0
        for y in self.data['y_val']:
            x += 1
            if isinstance(y, tuple):
                y = y[0]
                
            if c == True:
                context.line_to(self.x(x * self.zoom_x), self.y(y * self.zoom_y + self.x_axis_pos + self.spectra_space_bottom))
            else:
                context.line_to(self.x(x * self.print_temp['zoom_x']), self.y(y * self.print_temp['zoom_y'] + self.x_axis_pos + self.spectra_space_bottom))
            
        # Zeichnen!
        context.stroke()
    
    def ppm2x(self, ppm):
        """ Return the x value of a given ppm position. """
        if 'width' in self.print_temp:
            specWidth = self.oNMR.getCustomSpectrumWidth()
            specLeftPPM = self.oNMR.getCustomLeftBorder()
            specRightPPM = self.oNMR.getCustomRightBorder()
            zoom = self.print_temp['zoom_x']
        else:
            specWidth = self.oNMR.getSpectralWidth()
            specLeftPPM = self.oNMR.getOffset()
            specRightPPM = specLeftPPM - specWidth
            zoom = self.zoom_x
        
        resolution = len(self.data['y_val']) / specWidth
        diffPPM = specWidth - specLeftPPM
        
        x = specWidth - diffPPM - ppm
        x = x * resolution * zoom + self.padding
        
        return x
        
    def x2ppm(self, x):
        """ Return the ppm value of a given x position. """
        if 'width' in self.print_temp:
            specWidth = self.oNMR.getCustomSpectrumWidth()
            specLeftPPM = self.oNMR.getCustomLeftBorder()
            specRightPPM = self.oNMR.getCustomRightBorder()
            zoom = self.print_temp['zoom_x']
        else:
            specWidth = self.oNMR.getSpectralWidth()
            specLeftPPM = self.oNMR.getOffset()
            specRightPPM = specLeftPPM - specWidth
            zoom = self.zoom_x
        
        resolution = len(self.data['y_val']) / specWidth
        diffPPM = specWidth - specLeftPPM
        
        ppm = ((x - self.padding) / zoom) / resolution
        ppm = specWidth - ppm - diffPPM
        return ppm
        
    def x2Hz(self, x):
        """ Return the hertz value of a given x position. """
        ppm = self.x2ppm(x)
        hz = self.oNMR.ppm2Hz(ppm)
        return hz
        
    def i2x(self, i, ignorePadding = False):
        """ Return the x position of a the i-th value """
        if 'zoom_x' in self.print_temp:
            if ignorePadding == False:
                x = i * self.print_temp['zoom_x']
            else:
                x = i * self.print_temp['zoom_x'] + self.padding
        else:
            if ignorePadding == False:
                x = i * self.zoom_x
            else:
                x = i * self.zoom_x + self.padding
        return x
        
    def x2i(self, x, ignorePadding = False):
        if ignorePadding == False:
            i = x / self.zoom_x
        else:
            i = (x - self.padding) / self.zoom_x
        return int(round(i))
  
    def setMousepos(self, x, y, nobar = False):
        if nobar == False:
            self.mouse_area = self.MOUSE_AREA_NONE
            
            if self.mousePosX != x:
                # 1D
                self.mousePosX = x
                self.mousePosY = y
                
                self.clearMouseLayer()
                self.drawMouseLayer()
                self.refresh()
        else:
            self.mousePosX = x
            self.mousePosY = y
            
            if self.mousePosY < self.spectra_space_top:
                if self.mouse_area != self.MOUSE_AREA_PEAK:
                    self.o_sensitive_object = None
                    
                    # Clear other layers
                    if self.mouse_area == self.MOUSE_AREA_INTEGRAL:
                        self.redrawIntegralLayer()
                    
                    self.mouse_area = self.MOUSE_AREA_PEAK
            elif self.mousePosY > (self.iHeight - self.x_axis_pos):
                if self.mouse_area != self.MOUSE_AREA_INTEGRAL:
                    self.o_sensitive_object = None
                    
                    # Clear other layers
                    if self.mouse_area == self.MOUSE_AREA_PEAK:
                        self.redrawPeakLayer()
                    
                    self.mouse_area = self.MOUSE_AREA_INTEGRAL
            else:
                if self.mouse_area != self.MOUSE_AREA_NONE:
                    b = self.o_sensitive_object
                    self.o_sensitive_object = None
                    
                    # Clear other layers.
                    if self.mouse_area == self.MOUSE_AREA_INTEGRAL and b != None:
                        self.redrawIntegralLayer()
                    elif self.mouse_area == self.MOUSE_AREA_PEAK and b != None:
                        self.redrawPeakLayer()
                    
                    self.mouse_area = self.MOUSE_AREA_NONE
                    
            if self.mouse_area == self.MOUSE_AREA_INTEGRAL:
                # Now check if the mouse enters (or leaves) a sensitive area
                if self.mousePosX in self.l_sensitive_integral_area:
                    if self.l_sensitive_integral_area[int(self.mousePosX)] != self.o_sensitive_object:
                        self.o_sensitive_object = self.l_sensitive_integral_area[int(self.mousePosX)]
                        self.redrawIntegralLayer()
                    else:
                        # Still the same object. Skip...
                        pass
                else:
                    # Not over an integral I guess - but left one? Maybe?
                    if self.o_sensitive_object != None:
                        self.o_sensitive_object = None
                        self.redrawIntegralLayer()
            elif self.mouse_area == self.MOUSE_AREA_PEAK:
                if self.mousePosX in self.l_sensitive_peak_area:
                    if self.l_sensitive_peak_area[int(self.mousePosX)] != self.o_sensitive_object:
                        self.o_sensitive_object = self.l_sensitive_peak_area[int(self.mousePosX)]
                        self.redrawPeakLayer()
                    else:
                        # Still the same object. Skip...
                        pass
                else:
                    # Not over a peak I guess - but left one.
                    if self.o_sensitive_object != None:
                        self.o_sensitive_object = None
                        self.redrawPeakLayer()
                
    def clearMousepos(self, nobar = False):
        if nobar == False:
            self.mousePosX = -1
            self.mousePosY = -1
        else:
            self.mouse_area = self.MOUSE_AREA_NONE
            self.mousePosX = -1
            self.mousePosY = -1
            self.o_sensitive_object = None
            
    def clearMouseLayer(self):
        if self.mouseMode == 1:
            self.mouseLayer = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.iWidth, self.iHeight)
            self.mouseLayerCon = cairo.Context(self.mouseLayer)
            self.mouseLayerCon.set_antialias(False)
            self.mouseLayerCon.set_line_width(self.cairoLinewidth)
        elif self.mouseMode == 2:
            self.mouseLayer = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, self.iHeight)
            self.mouseLayerCon = cairo.Context(self.mouseLayer)
        
    def drawMouseLayer(self):
        if self.mouseLayerCon:
            if self.mouseMode == 1:
                self.oCairoOffC.set_source_rgba(1.0, 1.0, 1.0, 1.0)
                self.mouseLayerCon.move_to(self.x(self.mousePosX, ignorePadding = True)-0.5, self.y(0, ignorePadding = True))
                self.mouseLayerCon.line_to(self.x(self.mousePosX, ignorePadding = True)-0.5, self.y(0, True, ignorePadding = True))
                self.mouseLayerCon.stroke()
            elif self.mouseMode == 2:
                self.oCairoOffC.set_source_rgba(0.0, 1.0, 0.0, 1.0)
                self.mouseLayerCon.move_to(0.5, self.y(0, ignorePadding = True))
                self.mouseLayerCon.line_to(0.5, self.y(0, True, ignorePadding = True))
                self.mouseLayerCon.stroke()
    
    def showContextMenu(self, event, builder):
        if self.o_sensitive_object != None:
            if self.mouse_area == self.MOUSE_AREA_INTEGRAL:
                popup = builder.get_object('nmrAreaIntegralContextMenu').popup(None, None, None, event.button, event.time)
            elif self.mouse_area == self.MOUSE_AREA_PEAK:
                popup = builder.get_object('nmrAreaPeakContextMenu').popup(None, None, None, event.button, event.time)
        
    def getSensitiveObject(self):
        return self.o_sensitive_object
            
    def getPrintToContext(self, context, width, height):
        print "Print to an area with width = %s and height = %s" % (width, height)
        
        scale = height / self.STANDARD_HEIGHT
        
        print "Scale...: %s" % (scale, )
        print "User-Space-Coords: %s x %s" % (width / scale, self.STANDARD_HEIGHT)

        # self.print_temp = {'width' : width, 'height' : height}
        self.print_temp = {'width' : width / scale, 'height' : self.STANDARD_HEIGHT}
        
        context.scale(scale, scale)
        
        context.set_line_width(self.f_cairo_printlinewidth)
        
        # Step One: Draw Plot
        self.plot(context = context)
        self.drawPeakLayer(context = context)
        self.drawIntegralLayer(context = context)
        
        # This is important: RESET the temp vars. self.x and self.y check this variables and will
        # return wrong values for the normal plot.
        # I guess this whole applictation is messed up with hacks like these...
        self.print_temp = {}
        
    def x(self, x, reversed = False, ignorePadding = False):
        if 'width' in self.print_temp:
            if reversed:
                return self.print_temp['width'] - (x + self.padding) if ignorePadding == False else self.print_temp['width'] - x
            else:
                return x + self.padding if ignorePadding == False else x
        else:
            if reversed:
                return self.iWidth - (x + self.padding) if ignorePadding == False else self.iWidth - x
            else:
                return x + self.padding if ignorePadding == False else x
        
    def y(self, y, reversed = False, ignorePadding = False):
        if 'height' in self.print_temp:
            if reversed:
                return y + self.padding if ignorePadding == False else y
            else:
                return self.print_temp['height'] - (y + self.padding) if ignorePadding == False else self.print_temp['height'] - y
        else:
            if reversed:
                return y + self.padding if ignorePadding == False else y
            else:
                return self.iHeight - (y + self.padding) if ignorePadding == False else self.iHeight - y


